#include "mainwindow.h"
#include "ui_mainwindow.h"

#define PLUGINS_SUBFOLDER                   "/cvplugins/"
#define FILE_ON_DISK_DYNAMIC_PROPERTY       "absolute_file_path"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    loadSettings();

    urlSettings();

    populatePluginsMenu();

    ui->picView->setScene(&scene);
    scene.addItem(&srcPixmap);
    scene.addItem(&dstPixmap);
    ui->picView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);

}

MainWindow::~MainWindow()
{
    delete currentPlugin;
    delete ui;
}

void MainWindow::loadSettings()
{
    QSettings settings("Packt", "opencv", this);
    currentThemeFile = settings.value("currentThemeFile", "").toString();
    currentPluginFile = settings.value("currentPluginFile", "").toString();
}

void MainWindow::saveSettings()
{
    QSettings settings("Packt", "opencv", this);
    settings.setValue("currentThemeFile", currentThemeFile);
    settings.setValue("currentPluginFile", currentPluginFile);
}
/*
void MainWindow::closeEvent(QCloseEvent *event)
{
    int result = QMessageBox::warning(this, tr("Exit"), tr("Are you sure you want to exit?"), QMessageBox::Yes, QMessageBox::No);
    if(result == QMessageBox::Yes)
    {
        saveSettings();
        event->accept();
    }
    else
    {
        event->ignore();
    }
}
*/
void MainWindow::populatePluginsMenu()
{
    // Load all plugins and populate the menus
    QDir pluginsDir(qApp->applicationDirPath() + PLUGINS_SUBFOLDER);
    QFileInfoList pluginFiles = pluginsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    foreach(QFileInfo pluginFile, pluginFiles)
    {
        if(QLibrary::isLibrary(pluginFile.absoluteFilePath()))
        {
            QPluginLoader pluginLoader(pluginFile.absoluteFilePath(), this);
            CvPluginInterface *plugin = dynamic_cast<CvPluginInterface *>(pluginLoader.instance());
            if(plugin)
            {
                QAction *pluginAction = ui->menuPlugins->addAction(plugin->title());
                pluginAction->setProperty(FILE_ON_DISK_DYNAMIC_PROPERTY, pluginFile.absoluteFilePath());
                connect(pluginAction, SIGNAL(triggered(bool)), this, SLOT(onPluginActionTriggered(bool)));
                if(currentPluginFile == pluginFile.absoluteFilePath())
                {
                    pluginAction->trigger();
                }
            }
            else
            {
                QMessageBox::warning(this, tr("Warning"),
                                     QString(tr("Make sure %1 is a correct plugin for this application<br>"
                                                "and it's not in use by some other application!")).arg(pluginFile.fileName()));
            }
        }
        else
        {
            QMessageBox::warning(this, tr("Warning"),
                                 QString(tr("Make sure only plugins exist in %1 folder.<br>"
                                            "%2 is not a plugin."))
                                 .arg(PLUGINS_SUBFOLDER)
                                 .arg(pluginFile.fileName()));
        }
    }

    if(ui->menuPlugins->actions().count() <= 0)
    {
        QMessageBox::critical(this, tr("No Plugins"), QString(tr("This application cannot work without plugins!"
                                                                 "<br>Make sure that %1 folder exists "
                                                                 "in the same folder as the application<br>and that "
                                                                 "there are some plugins inside it")).arg(PLUGINS_SUBFOLDER));
        this->setEnabled(false);
    }
}

void MainWindow::onPluginActionTriggered(bool)
{
    if(!currentPlugin.isNull())
    {
        delete currentPlugin;
        delete currentPluginGui;
    }

    currentPluginFile = QObject::sender()->property(FILE_ON_DISK_DYNAMIC_PROPERTY).toString();
    currentPlugin = new QPluginLoader(currentPluginFile, this);
    currentPluginGui = new QWidget(this);
    ui->pluginLayout->addWidget(currentPluginGui);
    CvPluginInterface *currentPluginInstance = dynamic_cast<CvPluginInterface*>(currentPlugin->instance());

    if(currentPluginInstance)
    {
        currentPluginInstance->setupUi(currentPluginGui);
        connect(currentPlugin->instance(), SIGNAL(updateNeeded()), this, SLOT(onCurrentPluginUpdateNeeded()));
        connect(currentPlugin->instance(), SIGNAL(infoMessage(QString)), this, SLOT(onCurrentPluginInfoMessage(QString)));
        connect(currentPlugin->instance(), SIGNAL(errorMessage(QString)), this, SLOT(onCurrentPluginErrorMessage(QString)));

    }
}

void MainWindow::on_actionOpen_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this,
                                                    tr("Open Input Image"),
                                                    QDir::currentPath(),
                                                    tr("Images") + " (*.jpg *.png *.bmp)");

    using namespace cv;
    srcMat = imread(fileName.toStdString());
    if(!srcMat.empty())
    {
        tempMat = srcMat;
        srcImage = QImage(srcMat.data, srcMat.cols, srcMat.rows, srcMat.step, QImage::Format_RGB888);
        srcPixmap.setPixmap(QPixmap::fromImage(srcImage.rgbSwapped()));
        onCurrentPluginUpdateNeeded();
    }
    else if(!fileName.trimmed().isEmpty())
    {
        QMessageBox::critical(this,
                              tr("Error"),
                              tr("Make sure the image file exists "
                                 "and it is accessible!"));
    }
}

void MainWindow::on_actionSave_triggered()
{

    if(!ui->showOriginal->isChecked() && !dstMat.empty())
    {
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::currentPath(), "*.jpg;;*.png;;*.bmp");
        if(!fileName.isEmpty())
        {
            cv::imwrite(fileName.toStdString(), dstMat);
        }
    }
    else if(ui->showOriginal->isChecked() && !srcMat.empty())
    {
        QString fileName = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::currentPath(), "*.jpg;;*.png;;*.bmp");
        if(!fileName.isEmpty())
        {
            cv::imwrite(fileName.toStdString(), srcMat);
        }
    }
    else
    {
        QMessageBox::warning(this, tr("Warning"), tr("There is nothing to be saved!"));
    }
}

void MainWindow::on_actionExit_triggered()
{
    close();
}

void MainWindow::on_showOriginal_toggled(bool checked)
{
    srcPixmap.setVisible(checked);
    dstPixmap.setVisible(!checked);
}

void MainWindow::on_saveButton_clicked()
{
    //qDebug() << "clicked ";
    tempMat = dstMat.clone();
}

void MainWindow::onCurrentPluginUpdateNeeded()
{
    if(!tempMat.empty())
    {
        ui->menuPlugins->setEnabled(true);
        if(!currentPlugin.isNull())
        {
            CvPluginInterface *currentPluginInstance = dynamic_cast<CvPluginInterface*>(currentPlugin->instance());
            if(currentPluginInstance)
            {
                cv::TickMeter meter;
                meter.start();
                currentPluginInstance->processImage(tempMat, dstMat);
                meter.stop();
                qDebug() << "The process took " << meter.getTimeMilli() << "milliseconds";
                ui->infoLabel->setText(tr("The process took: %1 milliseconds")
                                       .arg(meter.getTimeMilli()));
            }
        }
        else
        {
            dstMat = tempMat.clone();
        }

        tempImage = QImage(tempMat.data, tempMat.cols, tempMat.rows, tempMat.step, QImage::Format_RGB888);
        tempPixmap.setPixmap(QPixmap::fromImage(tempImage.rgbSwapped()));

        dstImage = QImage(dstMat.data, dstMat.cols, dstMat.rows, dstMat.step, QImage::Format_RGB888);
        dstPixmap.setPixmap(QPixmap::fromImage(dstImage.rgbSwapped()));
    }
}

void MainWindow::urlSettings()
{
    connect(ui->urlLabel,SIGNAL(linkActivated(QString)),this,SLOT(openURL(QString)));
}

void MainWindow::openURL(QString url)
{
    QDesktopServices::openUrl(url);
}

void MainWindow::onCurrentPluginErrorMessage(QString msg)
{
    qDebug() << "Plugin Error Message : " << msg;
}

void MainWindow::onCurrentPluginInfoMessage(QString msg)
{
    qDebug() << "Plugin Info Message : " << msg;
}
